﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;


namespace visu
{
    public static class SearchExpression
    {

        public static Expression<Func<T, bool>> GetSearchExpression<T>(T FilterModels, string searchString, params string[] searchColumns)
        {

            return innerGetSearchExpression<T>(FilterModels, null, searchString, searchColumns);
        }


        public static Expression<Func<T, bool>> GetSearchExpression<T>(T FilterModels, List<SearchCondition> searchList, string searchString, params string[] searchColumns)
        {
            return innerGetSearchExpression<T>(FilterModels, searchList, searchString, searchColumns);
        }



        private static Expression<Func<T, bool>> innerGetSearchExpression<T>(T FilterModels, List<SearchCondition> searchList, string searchString, params string[] searchColumns)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(T), "r");
            ConstantExpression cons = Expression.Constant(1);
            Expression body = Expression.Equal(cons, cons);
            Type type = typeof(T);
            if (FilterModels != null)
            {

                ///组装过滤器
                PropertyInfo[] PropertyList = type.GetProperties();
                foreach (PropertyInfo item in PropertyList)
                {
                    string name = item.Name;
                    object value = item.GetValue(FilterModels, null);
                    if (value != null && value.ToString() != "0")
                    {
                        List<string> filterList = value.ToString().Split(',').ToList();
                        Expression subBody = Expression.NotEqual(cons, cons);
                        foreach (var filterField in filterList)
                        {
                            Expression left = Expression.Property(parameter, item);
                            Expression right = Expression.Constant(filterField);
                            Expression expr = Expression.Equal(left, right);
                            subBody = Expression.Or(subBody, expr);
                        }
                        body = Expression.And(body, subBody);
                    }


                }
            }

            if (searchList != null)
            {
                if (searchList.Count > 0)
                {

                    foreach (var item in searchList)
                    {

                        if (!string.IsNullOrEmpty(item.Field)&&!string.IsNullOrEmpty(item.Value)&&!string.IsNullOrEmpty(item.Operator))
                        {
                            var subBody = GetSubExpression<T>(item, parameter);
                            if (subBody != null)
                            {
                                body = Expression.And(body, subBody);
                            }
                        }
                    }
                }
            }

            //组装搜索框
            if (!string.IsNullOrEmpty(searchString))
            {
                Expression subBody = Expression.NotEqual(cons, cons);
                List<string> columnLists = searchColumns.ToList();
                foreach (string column in columnLists)
                {
                    if (!string.IsNullOrEmpty(column))
                    {
                        Expression searchBody = ConditonToExpression(column, parameter, type, searchString);
                        subBody = Expression.Or(subBody, searchBody);
                    }

                }
                body = Expression.And(body, subBody);
            }

            if (body != null)
            {
                Expression<Func<T, bool>> expr = Expression.
                    Lambda<Func<T, bool>>(body, parameter);
                return expr;
            }

            return Expression.Lambda<Func<T, bool>>(body, parameter);
        }


        private static Expression ConditonToExpression(string Field,string Operator, Expression parameter, Type type, string subValue)
        {
            Expression expr = null;

            PropertyInfo pi = type.GetProperty(Field);
            if (pi != null)
            {


                Expression left = Expression.Property(parameter, pi);
                Type pType = pi.PropertyType;
                Expression right = null;

                object value = null;
                value = Convert.ChangeType(subValue, pi.PropertyType);
                right = Expression.Constant(value);



                Expression expr1 = null;
                ConstantExpression zero = Expression.Constant(0);

                switch (Operator)
                {
                    case "=":
                        expr = Expression.Equal(left, right);

                        break;
                    case "<":
                        expr = Expression.LessThan(left, right);
                        break;
                    case "<=":
                        expr = Expression.LessThanOrEqual(left, right);
                        break;
                    case ">":
                        expr = Expression.GreaterThan(left, right);
                        break;
                    case ">=":
                        expr = Expression.GreaterThanOrEqual(left, right);
                        break;
                    case "!=":
                        expr = Expression.NotEqual(left, right);
                        break;
                    case "co":
                        expr = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
                        break;
                    case "noco":
                        MethodInfo methodNotLike = left.Type.GetMethod("Contains");
                        expr = Expression.Call(left, methodNotLike, new[] { right });
                        expr = Expression.Not(expr);
                        break;
                    case "start":
                        expr = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), right);

                        break;
                    case "end":
                        expr = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), right);
                        break;
                    case "str>=":
                        expr1 = Expression.Call(left, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), right);
                        expr = Expression.GreaterThanOrEqual(expr1, zero);
                        break;
                    case "str<=":
                        expr1 = Expression.Call(left, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), right);
                        expr = Expression.LessThanOrEqual(expr1, zero);
                        break;
                }
            }
            return expr;
        }

        private static Expression ConditonToExpression(string columnName, Expression parameter, Type type, string subValue)
        {
            Expression expr = null;
            PropertyInfo pi = type.GetProperty(columnName);
            if (pi != null)
            {
                Expression left = Expression.Property(parameter, pi);
                Type pType = pi.PropertyType;
                Expression right = null;

                object value = null;
                value = Convert.ChangeType(subValue, pi.PropertyType);
                right = Expression.Constant(value);


                expr = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);


            }
            return expr;
        }

        /// <summary>
        /// 输入排序字段和相应类，返回排序表达式
        /// </summary>
        /// <typeparam name="类名"></typeparam>
        /// <param name="排序字段"></param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderByProperty,
string desc) where T : class
        {

            string methodName = string.IsNullOrEmpty(desc) ? "OrderBy" : desc == "asc" ? "OrderBy" : "OrderByDescending";
            ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
            MemberExpression property = Expression.Property(parameter, orderByProperty);
            LambdaExpression lambda = Expression.Lambda(property, parameter);

            Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
                                            new Type[] { source.ElementType, property.Type },
                                            source.Expression, Expression.Quote(lambda));
            return source.Provider.CreateQuery<T>(methodCallExpression);
        }




        private static Expression GetSubExpression<T>(SearchCondition high, ParameterExpression parameter)
        {

            ConstantExpression cons = Expression.Constant(1);
            Expression body = Expression.NotEqual(cons, cons);
            Type type = typeof(T);

            if (!string.IsNullOrEmpty(high.Field) && !string.IsNullOrEmpty(high.Operator))
            {
                for (int i = 0; i < high.Field.Split(',').Length; i++)
                {
                    for (int j = 0; j < high.Value.Split(',').Length; j++)
                    {
                        Expression subRight = ConditonToExpression(high.Field.Split(',')[i], high.Operator, parameter, type, high.Value.Split(',')[j]);
                        if (subRight != null)
                        {
                            body = Expression.Or(body, subRight);
                        }

                    }
                }

            }

            return body;
        }



    }
}